

<!DOCTYPE html>
<html class="writer-html5" lang="en" >
<head>
  <meta charset="utf-8" />
  
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  
  <title>Ceph SQLite VFS &mdash; Ceph Documentation</title>
  

  
  <link rel="stylesheet" href="../../../_static/ceph.css" type="text/css" />
  <link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
  <link rel="stylesheet" href="../../../_static/graphviz.css" type="text/css" />
  <link rel="stylesheet" href="../../../_static/css/custom.css" type="text/css" />

  
  
    <link rel="shortcut icon" href="../../../_static/favicon.ico"/>
  

  
  

  

  
  <!--[if lt IE 9]>
    <script src="../../../_static/js/html5shiv.min.js"></script>
  <![endif]-->
  
    
      <script type="text/javascript" id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
        <script src="../../../_static/jquery.js"></script>
        <script src="../../../_static/underscore.js"></script>
        <script src="../../../_static/doctools.js"></script>
    
    <script type="text/javascript" src="../../../_static/js/theme.js"></script>

    
    <link rel="index" title="Index" href="../../../genindex/" />
    <link rel="search" title="Search" href="../../../search/" />
    <link rel="next" title="SDK for Ceph Object Classes" href="../objclass-sdk/" />
    <link rel="prev" title="Librados (Python)" href="../python/" /> 
</head>

<body class="wy-body-for-nav">

   
  <header class="top-bar">
    

















<div role="navigation" aria-label="breadcrumbs navigation">

  <ul class="wy-breadcrumbs">
    
      <li><a href="../../../" class="icon icon-home"></a> &raquo;</li>
        
          <li><a href="../../">Ceph 存储集群</a> &raquo;</li>
        
          <li><a href="../">Ceph 存储集群 API</a> &raquo;</li>
        
      <li>Ceph SQLite VFS</li>
    
    
      <li class="wy-breadcrumbs-aside">
        
          
            <a href="../../../_sources/rados/api/libcephsqlite.rst.txt" rel="nofollow"> View page source</a>
          
        
      </li>
    
  </ul>

  
  <hr/>
</div>
  </header>
  <div class="wy-grid-for-nav">
    
    <nav data-toggle="wy-nav-shift" class="wy-nav-side">
      <div class="wy-side-scroll">
        <div class="wy-side-nav-search"  style="background: #eee" >
          

          
            <a href="../../../">
          

          
            
            <img src="../../../_static/logo.png" class="logo" alt="Logo"/>
          
          </a>

          

          
<div role="search">
  <form id="rtd-search-form" class="wy-form" action="../../../search/" method="get">
    <input type="text" name="q" placeholder="Search docs" />
    <input type="hidden" name="check_keywords" value="yes" />
    <input type="hidden" name="area" value="default" />
  </form>
</div>

          
        </div>

        
        <div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
          
            
            
              
            
            
              <ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../../../start/intro/">Ceph 简介</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../install/">安装 Ceph</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../cephadm/">Cephadm</a></li>
<li class="toctree-l1 current"><a class="reference internal" href="../../">Ceph 存储集群</a><ul class="current">
<li class="toctree-l2"><a class="reference internal" href="../../configuration/">配置</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../operations/">运维</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../man/">    手册页</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../troubleshooting/">故障排除</a></li>
<li class="toctree-l2 current"><a class="reference internal" href="../">APIs</a><ul class="current">
<li class="toctree-l3"><a class="reference internal" href="../librados-intro/">librados 简介</a></li>
<li class="toctree-l3"><a class="reference internal" href="../librados/">librados (C)</a></li>
<li class="toctree-l3"><a class="reference internal" href="../libradospp/">librados (C++)</a></li>
<li class="toctree-l3"><a class="reference internal" href="../python/">librados (Python)</a></li>
<li class="toctree-l3 current"><a class="current reference internal" href="#">libcephsqlite (SQLite)</a><ul>
<li class="toctree-l4"><a class="reference internal" href="#usage">Usage</a></li>
<li class="toctree-l4"><a class="reference internal" href="#user">User</a></li>
<li class="toctree-l4"><a class="reference internal" href="#page-size">Page Size</a></li>
<li class="toctree-l4"><a class="reference internal" href="#cache">Cache</a></li>
<li class="toctree-l4"><a class="reference internal" href="#journal-persistence">Journal Persistence</a></li>
<li class="toctree-l4"><a class="reference internal" href="#exclusive-lock-mode">Exclusive Lock Mode</a></li>
<li class="toctree-l4"><a class="reference internal" href="#wal-journal">WAL Journal</a></li>
<li class="toctree-l4"><a class="reference internal" href="#performance-notes">Performance Notes</a></li>
<li class="toctree-l4"><a class="reference internal" href="#recommended-use-cases">Recommended Use-Cases</a></li>
<li class="toctree-l4"><a class="reference internal" href="#parallel-access">Parallel Access</a></li>
<li class="toctree-l4"><a class="reference internal" href="#export-or-extract-database-out-of-rados">Export or Extract Database out of RADOS</a></li>
<li class="toctree-l4"><a class="reference internal" href="#temporary-tables">Temporary Tables</a></li>
<li class="toctree-l4"><a class="reference internal" href="#breaking-locks">Breaking Locks</a></li>
<li class="toctree-l4"><a class="reference internal" href="#how-to-corrupt-your-database">How to Corrupt Your Database</a></li>
<li class="toctree-l4"><a class="reference internal" href="#performance-statistics">Performance Statistics</a></li>
</ul>
</li>
<li class="toctree-l3"><a class="reference internal" href="../objclass-sdk/">object class</a></li>
</ul>
</li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="../../../cephfs/">Ceph 文件系统</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../rbd/">Ceph 块设备</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../radosgw/">Ceph 对象网关</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../mgr/">Ceph 管理器守护进程</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../mgr/dashboard/">Ceph 仪表盘</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../api/">API 文档</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../architecture/">体系结构</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../dev/developer_guide/">开发者指南</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../dev/internals/">Ceph 内幕</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../governance/">项目管理</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../foundation/">Ceph 基金会</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../ceph-volume/">ceph-volume</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../releases/general/">Ceph 版本（总目录）</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../releases/">Ceph 版本（索引）</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../security/">Security</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../glossary/">Ceph 术语</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../jaegertracing/">Tracing</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../translation_cn/">中文版翻译资源</a></li>
</ul>

            
          
        </div>
        
      </div>
    </nav>

    <section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">

      
      <nav class="wy-nav-top" aria-label="top navigation">
        
          <i data-toggle="wy-nav-top" class="fa fa-bars"></i>
          <a href="../../../">Ceph</a>
        
      </nav>


      <div class="wy-nav-content">
        
        <div class="rst-content">
        
          <div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
           <div itemprop="articleBody">
            
<div id="dev-warning" class="admonition note">
  <p class="first admonition-title">Notice</p>
  <p class="last">This document is for a development version of Ceph.</p>
</div>
  <div id="docubetter" align="right" style="padding: 5px; font-weight: bold;">
    <a href="https://pad.ceph.com/p/Report_Documentation_Bugs">Report a Documentation Bug</a>
  </div>

  
  <div class="section" id="ceph-sqlite-vfs">
<span id="libcephsqlite"></span><h1>Ceph SQLite VFS<a class="headerlink" href="#ceph-sqlite-vfs" title="Permalink to this headline">¶</a></h1>
<p>This <a class="reference external" href="https://www.sqlite.org/vfs.html">SQLite VFS</a> may be used for storing and accessing a <a class="reference external" href="https://sqlite.org/index.html">SQLite</a> database
backed by RADOS. This allows you to fully decentralize your database using
Ceph’s object store for improved availability, accessibility, and use of
storage.</p>
<p>Note what this is not: a distributed SQL engine. SQLite on RADOS can be thought
of like RBD as compared to CephFS: RBD puts a disk image on RADOS for the
purposes of exclusive access by a machine and generally does not allow parallel
access by other machines; on the other hand, CephFS allows fully distributed
access to a file system from many client mounts. SQLite on RADOS is meant to be
accessed by a single SQLite client database connection at a given time.  The
database may be manipulated safely by multiple clients only in a serial fashion
controlled by RADOS locks managed by the Ceph SQLite VFS.</p>
<div class="section" id="usage">
<h2>Usage<a class="headerlink" href="#usage" title="Permalink to this headline">¶</a></h2>
<p>Normal unmodified applications (including the sqlite command-line toolset
binary) may load the <em>ceph</em> VFS using the <a class="reference external" href="https://sqlite.org/c3ref/load_extension.html">SQLite Extension Loading API</a>.</p>
<div class="highlight-sql notranslate"><div class="highlight"><pre><span></span><span class="p">.</span><span class="k">LOAD</span> <span class="n">libcephsqlite</span><span class="p">.</span><span class="n">so</span>
</pre></div>
</div>
<p>or during the invocation of <code class="docutils literal notranslate"><span class="pre">sqlite3</span></code></p>
<div class="highlight-sh notranslate"><div class="highlight"><pre><span></span>sqlite3 -cmd <span class="s1">&#39;.load libcephsqlite.so&#39;</span>
</pre></div>
</div>
<p>A database file is formatted as a SQLite URI:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>file:///&lt;&quot;*&quot;poolid|poolname&gt;:[namespace]/&lt;dbname&gt;?vfs=ceph
</pre></div>
</div>
<p>The RADOS <code class="docutils literal notranslate"><span class="pre">namespace</span></code> is optional. Note the triple <code class="docutils literal notranslate"><span class="pre">///</span></code> in the path. The URI
authority must be empty or localhost in SQLite. Only the path part of the URI
is parsed. For this reason, the URI will not parse properly if you only use two
<code class="docutils literal notranslate"><span class="pre">//</span></code>.</p>
<p>A complete example of (optionally) creating a database and opening:</p>
<div class="highlight-sh notranslate"><div class="highlight"><pre><span></span>sqlite3 -cmd <span class="s1">&#39;.load libcephsqlite.so&#39;</span> -cmd <span class="s1">&#39;.open file:///foo:bar/baz.db?vfs=ceph&#39;</span>
</pre></div>
</div>
<p>Note you cannot specify the database file as the normal positional argument to
<code class="docutils literal notranslate"><span class="pre">sqlite3</span></code>. This is because the <code class="docutils literal notranslate"><span class="pre">.load</span> <span class="pre">libcephsqlite.so</span></code> command is applied
after opening the database, but opening the database depends on the extension
being loaded first.</p>
<p>An example passing the pool integer id and no RADOS namespace:</p>
<div class="highlight-sh notranslate"><div class="highlight"><pre><span></span>sqlite3 -cmd <span class="s1">&#39;.load libcephsqlite.so&#39;</span> -cmd <span class="s1">&#39;.open file:///*2:/baz.db?vfs=ceph&#39;</span>
</pre></div>
</div>
<p>Like other Ceph tools, the <em>ceph</em> VFS looks at some environment variables that
help with configuring which Ceph cluster to communicate with and which
credential to use. Here would be a typical configuration:</p>
<div class="highlight-sh notranslate"><div class="highlight"><pre><span></span><span class="nb">export</span> <span class="nv">CEPH_CONF</span><span class="o">=</span>/path/to/ceph.conf
<span class="nb">export</span> <span class="nv">CEPH_KEYRING</span><span class="o">=</span>/path/to/ceph.keyring
<span class="nb">export</span> <span class="nv">CEPH_ARGS</span><span class="o">=</span><span class="s1">&#39;--id myclientid&#39;</span>
./runmyapp
<span class="c1"># or</span>
sqlite3 -cmd <span class="s1">&#39;.load libcephsqlite.so&#39;</span> -cmd <span class="s1">&#39;.open file:///foo:bar/baz.db?vfs=ceph&#39;</span>
</pre></div>
</div>
<p>The default operation would look at the standard Ceph configuration file path
using the <code class="docutils literal notranslate"><span class="pre">client.admin</span></code> user.</p>
</div>
<div class="section" id="user">
<h2>User<a class="headerlink" href="#user" title="Permalink to this headline">¶</a></h2>
<p>The <em>ceph</em> VFS requires a user credential with read access to the monitors, the
ability to blocklist dead clients of the database, and access to the OSDs
hosting the database. This can be done with authorizations as simply as:</p>
<div class="highlight-sh notranslate"><div class="highlight"><pre><span></span>ceph auth get-or-create client.X mon <span class="s1">&#39;allow r, allow command &quot;osd blocklist&quot; with blocklistop=add&#39;</span> osd <span class="s1">&#39;allow rwx&#39;</span>
</pre></div>
</div>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>The terminology change from <code class="docutils literal notranslate"><span class="pre">blacklist</span></code> to <code class="docutils literal notranslate"><span class="pre">blocklist</span></code>; older clusters may require using the old terms.</p>
</div>
<p>You may also simplify using the <code class="docutils literal notranslate"><span class="pre">simple-rados-client-with-blocklist</span></code> profile:</p>
<div class="highlight-sh notranslate"><div class="highlight"><pre><span></span>ceph auth get-or-create client.X mon <span class="s1">&#39;profile simple-rados-client-with-blocklist&#39;</span> osd <span class="s1">&#39;allow rwx&#39;</span>
</pre></div>
</div>
<p>To learn why blocklisting is necessary, see <a class="reference internal" href="#libcephsqlite-corrupt"><span class="std std-ref">How to Corrupt Your Database</span></a>.</p>
</div>
<div class="section" id="page-size">
<h2>Page Size<a class="headerlink" href="#page-size" title="Permalink to this headline">¶</a></h2>
<p>SQLite allows configuring the page size prior to creating a new database. It is
advisable to increase this config to 65536 (64K) when using RADOS backed
databases to reduce the number of OSD reads/writes and thereby improve
throughput and latency.</p>
<div class="highlight-sql notranslate"><div class="highlight"><pre><span></span><span class="n">PRAGMA</span> <span class="n">page_size</span> <span class="o">=</span> <span class="mi">65536</span>
</pre></div>
</div>
<p>You may also try other values according to your application needs but note that
64K is the max imposed by SQLite.</p>
</div>
<div class="section" id="cache">
<h2>Cache<a class="headerlink" href="#cache" title="Permalink to this headline">¶</a></h2>
<p>The ceph VFS does not do any caching of reads or buffering of writes. Instead,
and more appropriately, the SQLite page cache is used. You may find it is too small
for most workloads and should therefore increase it significantly:</p>
<div class="highlight-sql notranslate"><div class="highlight"><pre><span></span><span class="n">PRAGMA</span> <span class="n">cache_size</span> <span class="o">=</span> <span class="mi">4096</span>
</pre></div>
</div>
<p>Which will cache 4096 pages or 256MB (with 64K <code class="docutils literal notranslate"><span class="pre">page_cache</span></code>).</p>
</div>
<div class="section" id="journal-persistence">
<h2>Journal Persistence<a class="headerlink" href="#journal-persistence" title="Permalink to this headline">¶</a></h2>
<p>By default, SQLite deletes the journal for every transaction. This can be
expensive as the <em>ceph</em> VFS must delete every object backing the journal for each
transaction. For this reason, it is much faster and simpler to ask SQLite to
<strong>persist</strong> the journal. In this mode, SQLite will invalidate the journal via a
write to its header. This is done as:</p>
<div class="highlight-sql notranslate"><div class="highlight"><pre><span></span><span class="n">PRAGMA</span> <span class="n">journal_mode</span> <span class="o">=</span> <span class="n">PERSIST</span>
</pre></div>
</div>
<p>The cost of this may be increased unused space according to the high-water size
of the rollback journal (based on transaction type and size).</p>
</div>
<div class="section" id="exclusive-lock-mode">
<h2>Exclusive Lock Mode<a class="headerlink" href="#exclusive-lock-mode" title="Permalink to this headline">¶</a></h2>
<p>SQLite operates in a <code class="docutils literal notranslate"><span class="pre">NORMAL</span></code> locking mode where each transaction requires
locking the backing database file. This can add unnecessary overhead to
transactions when you know there’s only ever one user of the database at a
given time. You can have SQLite lock the database once for the duration of the
connection using:</p>
<div class="highlight-sql notranslate"><div class="highlight"><pre><span></span><span class="n">PRAGMA</span> <span class="n">locking_mode</span> <span class="o">=</span> <span class="k">EXCLUSIVE</span>
</pre></div>
</div>
<p>This can more than <strong>halve</strong> the time taken to perform a transaction. Keep in
mind this prevents other clients from accessing the database.</p>
<p>In this locking mode, each write transaction to the database requires 3
synchronization events: once to write to the journal, another to write to the
database file, and a final write to invalidate the journal header (in
<code class="docutils literal notranslate"><span class="pre">PERSIST</span></code> journaling mode).</p>
</div>
<div class="section" id="wal-journal">
<h2>WAL Journal<a class="headerlink" href="#wal-journal" title="Permalink to this headline">¶</a></h2>
<p>The <a class="reference external" href="https://sqlite.org/wal.html">WAL Journal Mode</a> is only available when SQLite is operating in exclusive
lock mode. This is because it requires shared memory communication with other
readers and writers when in the <code class="docutils literal notranslate"><span class="pre">NORMAL</span></code> locking mode.</p>
<p>As with local disk databases, WAL mode may significantly reduce small
transaction latency. Testing has shown it can provide more than 50% speedup
over persisted rollback journals in exclusive locking mode. You can expect
around 150-250 transactions per second depending on size.</p>
</div>
<div class="section" id="performance-notes">
<h2>Performance Notes<a class="headerlink" href="#performance-notes" title="Permalink to this headline">¶</a></h2>
<p>The filing backend for the database on RADOS is asynchronous as much as
possible.  Still, performance can be anywhere from 3x-10x slower than a local
database on SSD. Latency can be a major factor. It is advisable to be familiar
with SQL transactions and other strategies for efficient database updates.
Depending on the performance of the underlying pool, you can expect small
transactions to take up to 30 milliseconds to complete. If you use the
<code class="docutils literal notranslate"><span class="pre">EXCLUSIVE</span></code> locking mode, it can be reduced further to 15 milliseconds per
transaction. A WAL journal in <code class="docutils literal notranslate"><span class="pre">EXCLUSIVE</span></code> locking mode can further reduce
this as low as ~2-5 milliseconds (or the time to complete a RADOS write; you
won’t get better than that!).</p>
<p>There is no limit to the size of a SQLite database on RADOS imposed by the Ceph
VFS. There are standard <a class="reference external" href="https://www.sqlite.org/limits.html">SQLite Limits</a> to be aware of, notably the maximum
database size of 281 TB. Large databases may or may not be performant on Ceph.
Experimentation for your own use-case is advised.</p>
<p>Be aware that read-heavy queries could take significant amounts of time as
reads are necessarily synchronous (due to the VFS API). No readahead is yet
performed by the VFS.</p>
</div>
<div class="section" id="recommended-use-cases">
<h2>Recommended Use-Cases<a class="headerlink" href="#recommended-use-cases" title="Permalink to this headline">¶</a></h2>
<p>The original purpose of this module was to support saving relational or large
data in RADOS which needs to span multiple objects. Many current applications
with trivial state try to use RADOS omap storage on a single object but this
cannot scale without striping data across multiple objects. Unfortunately, it
is non-trivial to design a store spanning multiple objects which is consistent
and also simple to use. SQLite can be used to bridge that gap.</p>
</div>
<div class="section" id="parallel-access">
<h2>Parallel Access<a class="headerlink" href="#parallel-access" title="Permalink to this headline">¶</a></h2>
<p>The VFS does not yet support concurrent readers. All database access is protected
by a single exclusive lock.</p>
</div>
<div class="section" id="export-or-extract-database-out-of-rados">
<h2>Export or Extract Database out of RADOS<a class="headerlink" href="#export-or-extract-database-out-of-rados" title="Permalink to this headline">¶</a></h2>
<p>The database is striped on RADOS and can be extracted using the RADOS cli toolset.</p>
<div class="highlight-sh notranslate"><div class="highlight"><pre><span></span>rados --pool<span class="o">=</span>foo --striper get bar.db local-bar.db
rados --pool<span class="o">=</span>foo --striper get bar.db-journal local-bar.db-journal
sqlite3 local-bar.db ...
</pre></div>
</div>
<p>Keep in mind the rollback journal is also striped and will need to be extracted
as well if the database was in the middle of a transaction. If you’re using
WAL, that journal will need to be extracted as well.</p>
<p>Keep in mind that extracting the database using the striper uses the same RADOS
locks as those used by the <em>ceph</em> VFS. However, the journal file locks are not
used by the <em>ceph</em> VFS (SQLite only locks the main database file) so there is a
potential race with other SQLite clients when extracting both files. That could
result in fetching a corrupt journal.</p>
<p>Instead of manually extracting the files, it would be more advisable to use the
<a class="reference external" href="https://www.sqlite.org/backup.html">SQLite Backup</a> mechanism instead.</p>
</div>
<div class="section" id="temporary-tables">
<h2>Temporary Tables<a class="headerlink" href="#temporary-tables" title="Permalink to this headline">¶</a></h2>
<p>Temporary tables backed by the ceph VFS are not supported. The main reason for
this is that the VFS lacks context about where it should put the database, i.e.
which RADOS pool. The persistent database associated with the temporary
database is not communicated via the SQLite VFS API.</p>
<p>Instead, it’s suggested to attach a secondary local or <a class="reference external" href="https://www.sqlite.org/inmemorydb.html">In-Memory Database</a>
and put the temporary tables there. Alternatively, you may set a connection
pragma:</p>
<div class="highlight-sql notranslate"><div class="highlight"><pre><span></span><span class="n">PRAGMA</span> <span class="n">temp_store</span><span class="o">=</span><span class="n">memory</span>
</pre></div>
</div>
</div>
<div class="section" id="breaking-locks">
<span id="libcephsqlite-breaking-locks"></span><h2>Breaking Locks<a class="headerlink" href="#breaking-locks" title="Permalink to this headline">¶</a></h2>
<p>Access to the database file is protected by an exclusive lock on the first
object stripe of the database. If the application fails without unlocking the
database (e.g. a segmentation fault), the lock is not automatically unlocked,
even if the client connection is blocklisted afterward. Eventually, the lock
will timeout subject to the configurations:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">cephsqlite_lock_renewal_timeout</span> <span class="o">=</span> <span class="mi">30000</span>
</pre></div>
</div>
<p>The timeout is in milliseconds. Once the timeout is reached, the OSD will
expire the lock and allow clients to relock. When this occurs, the database
will be recovered by SQLite and the in-progress transaction rolled back. The
new client recovering the database will also blocklist the old client to
prevent potential database corruption from rogue writes.</p>
<p>The holder of the exclusive lock on the database will periodically renew the
lock so it does not lose the lock. This is necessary for large transactions or
database connections operating in <code class="docutils literal notranslate"><span class="pre">EXCLUSIVE</span></code> locking mode. The lock renewal
interval is adjustable via:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">cephsqlite_lock_renewal_interval</span> <span class="o">=</span> <span class="mi">2000</span>
</pre></div>
</div>
<p>This configuration is also in units of milliseconds.</p>
<p>It is possible to break the lock early if you know the client is gone for good
(e.g. blocklisted). This allows restoring database access to clients
immediately. For example:</p>
<div class="highlight-sh notranslate"><div class="highlight"><pre><span></span>$ rados --pool<span class="o">=</span>foo --namespace bar lock info baz.db.0000000000000000 striper.lock
<span class="o">{</span><span class="s2">&quot;name&quot;</span>:<span class="s2">&quot;striper.lock&quot;</span>,<span class="s2">&quot;type&quot;</span>:<span class="s2">&quot;exclusive&quot;</span>,<span class="s2">&quot;tag&quot;</span>:<span class="s2">&quot;&quot;</span>,<span class="s2">&quot;lockers&quot;</span>:<span class="o">[{</span><span class="s2">&quot;name&quot;</span>:<span class="s2">&quot;client.4463&quot;</span>,<span class="s2">&quot;cookie&quot;</span>:<span class="s2">&quot;555c7208-db39-48e8-a4d7-3ba92433a41a&quot;</span>,<span class="s2">&quot;description&quot;</span>:<span class="s2">&quot;SimpleRADOSStriper&quot;</span>,<span class="s2">&quot;expiration&quot;</span>:<span class="s2">&quot;0.000000&quot;</span>,<span class="s2">&quot;addr&quot;</span>:<span class="s2">&quot;127.0.0.1:0/1831418345&quot;</span><span class="o">}]}</span>

$ rados --pool<span class="o">=</span>foo --namespace bar lock <span class="nb">break</span> baz.db.0000000000000000 striper.lock client.4463 --lock-cookie 555c7208-db39-48e8-a4d7-3ba92433a41a
</pre></div>
</div>
</div>
<div class="section" id="how-to-corrupt-your-database">
<span id="libcephsqlite-corrupt"></span><h2>How to Corrupt Your Database<a class="headerlink" href="#how-to-corrupt-your-database" title="Permalink to this headline">¶</a></h2>
<p>There is the usual reading on <a class="reference external" href="https://www.sqlite.org/howtocorrupt.html">How to Corrupt Your SQLite Database</a> that you
should review before using this tool. To add to that, the most likely way you
may corrupt your database is by a rogue process transiently losing network
connectivity and then resuming its work. The exclusive RADOS lock it held will
be lost but it cannot know that immediately. Any work it might do after
regaining network connectivity could corrupt the database.</p>
<p>The <em>ceph</em> VFS library defaults do not allow for this scenario to occur. The Ceph
VFS will blocklist the last owner of the exclusive lock on the database if it
detects incomplete cleanup.</p>
<p>By blocklisting the old client, it’s no longer possible for the old client to
resume its work on the database when it returns (subject to blocklist
expiration, 3600 seconds by default). To turn off blocklisting the prior client, change:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">cephsqlite_blocklist_dead_locker</span> <span class="o">=</span> <span class="n">false</span>
</pre></div>
</div>
<p>Do NOT do this unless you know database corruption cannot result due to other
guarantees. If this config is true (the default), the <em>ceph</em> VFS will cowardly
fail if it cannot blocklist the prior instance (due to lack of authorization,
for example).</p>
<p>One example where out-of-band mechanisms exist to blocklist the last dead
holder of the exclusive lock on the database is in the <code class="docutils literal notranslate"><span class="pre">ceph-mgr</span></code>. The
monitors are made aware of the RADOS connection used for the <em>ceph</em> VFS and will
blocklist the instance during <code class="docutils literal notranslate"><span class="pre">ceph-mgr</span></code> failover. This prevents a zombie
<code class="docutils literal notranslate"><span class="pre">ceph-mgr</span></code> from continuing work and potentially corrupting the database. For
this reason, it is not necessary for the <em>ceph</em> VFS to do the blocklist command
in the new instance of the <code class="docutils literal notranslate"><span class="pre">ceph-mgr</span></code> (but it still does so, harmlessly).</p>
<p>To blocklist the <em>ceph</em> VFS manually, you may see the instance address of the
<em>ceph</em> VFS using the <code class="docutils literal notranslate"><span class="pre">ceph_status</span></code> SQL function:</p>
<div class="highlight-sql notranslate"><div class="highlight"><pre><span></span><span class="k">SELECT</span> <span class="n">ceph_status</span><span class="p">();</span>
</pre></div>
</div>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">{</span><span class="s2">&quot;id&quot;</span><span class="p">:</span><span class="mi">788461300</span><span class="p">,</span><span class="s2">&quot;addr&quot;</span><span class="p">:</span><span class="s2">&quot;172.21.10.4:0/1472139388&quot;</span><span class="p">}</span>
</pre></div>
</div>
<p>You may easily manipulate that information using the <a class="reference external" href="https://www.sqlite.org/json1.html">JSON1 extension</a>:</p>
<div class="highlight-sql notranslate"><div class="highlight"><pre><span></span><span class="k">SELECT</span> <span class="n">json_extract</span><span class="p">(</span><span class="n">ceph_status</span><span class="p">(),</span> <span class="s1">&#39;$.addr&#39;</span><span class="p">);</span>
</pre></div>
</div>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="mf">172.21.10.4</span><span class="p">:</span><span class="mi">0</span><span class="o">/</span><span class="mi">3563721180</span>
</pre></div>
</div>
<p>This is the address you would pass to the ceph blocklist command:</p>
<div class="highlight-sh notranslate"><div class="highlight"><pre><span></span>ceph osd blocklist add <span class="m">172</span>.21.10.4:0/3082314560
</pre></div>
</div>
</div>
<div class="section" id="performance-statistics">
<h2>Performance Statistics<a class="headerlink" href="#performance-statistics" title="Permalink to this headline">¶</a></h2>
<p>The <em>ceph</em> VFS provides a SQLite function, <code class="docutils literal notranslate"><span class="pre">ceph_perf</span></code>, for querying the
performance statistics of the VFS. The data is from “performance counters” as
in other Ceph services normally queried via an admin socket.</p>
<div class="highlight-sql notranslate"><div class="highlight"><pre><span></span><span class="k">SELECT</span> <span class="n">ceph_perf</span><span class="p">();</span>
</pre></div>
</div>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">{</span><span class="s2">&quot;libcephsqlite_vfs&quot;</span><span class="p">:{</span><span class="s2">&quot;op_open&quot;</span><span class="p">:{</span><span class="s2">&quot;avgcount&quot;</span><span class="p">:</span><span class="mi">2</span><span class="p">,</span><span class="s2">&quot;sum&quot;</span><span class="p">:</span><span class="mf">0.150001291</span><span class="p">,</span><span class="s2">&quot;avgtime&quot;</span><span class="p">:</span><span class="mf">0.075000645</span><span class="p">},</span><span class="s2">&quot;op_delete&quot;</span><span class="p">:{</span><span class="s2">&quot;avgcount&quot;</span><span class="p">:</span><span class="mi">0</span><span class="p">,</span><span class="s2">&quot;sum&quot;</span><span class="p">:</span><span class="mf">0.000000000</span><span class="p">,</span><span class="s2">&quot;avgtime&quot;</span><span class="p">:</span><span class="mf">0.000000000</span><span class="p">},</span><span class="s2">&quot;op_access&quot;</span><span class="p">:{</span><span class="s2">&quot;avgcount&quot;</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span><span class="s2">&quot;sum&quot;</span><span class="p">:</span><span class="mf">0.003000026</span><span class="p">,</span><span class="s2">&quot;avgtime&quot;</span><span class="p">:</span><span class="mf">0.003000026</span><span class="p">},</span><span class="s2">&quot;op_fullpathname&quot;</span><span class="p">:{</span><span class="s2">&quot;avgcount&quot;</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span><span class="s2">&quot;sum&quot;</span><span class="p">:</span><span class="mf">0.064000551</span><span class="p">,</span><span class="s2">&quot;avgtime&quot;</span><span class="p">:</span><span class="mf">0.064000551</span><span class="p">},</span><span class="s2">&quot;op_currenttime&quot;</span><span class="p">:{</span><span class="s2">&quot;avgcount&quot;</span><span class="p">:</span><span class="mi">0</span><span class="p">,</span><span class="s2">&quot;sum&quot;</span><span class="p">:</span><span class="mf">0.000000000</span><span class="p">,</span><span class="s2">&quot;avgtime&quot;</span><span class="p">:</span><span class="mf">0.000000000</span><span class="p">},</span><span class="s2">&quot;opf_close&quot;</span><span class="p">:{</span><span class="s2">&quot;avgcount&quot;</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span><span class="s2">&quot;sum&quot;</span><span class="p">:</span><span class="mf">0.000000000</span><span class="p">,</span><span class="s2">&quot;avgtime&quot;</span><span class="p">:</span><span class="mf">0.000000000</span><span class="p">},</span><span class="s2">&quot;opf_read&quot;</span><span class="p">:{</span><span class="s2">&quot;avgcount&quot;</span><span class="p">:</span><span class="mi">3</span><span class="p">,</span><span class="s2">&quot;sum&quot;</span><span class="p">:</span><span class="mf">0.036000310</span><span class="p">,</span><span class="s2">&quot;avgtime&quot;</span><span class="p">:</span><span class="mf">0.012000103</span><span class="p">},</span><span class="s2">&quot;opf_write&quot;</span><span class="p">:{</span><span class="s2">&quot;avgcount&quot;</span><span class="p">:</span><span class="mi">0</span><span class="p">,</span><span class="s2">&quot;sum&quot;</span><span class="p">:</span><span class="mf">0.000000000</span><span class="p">,</span><span class="s2">&quot;avgtime&quot;</span><span class="p">:</span><span class="mf">0.000000000</span><span class="p">},</span><span class="s2">&quot;opf_truncate&quot;</span><span class="p">:{</span><span class="s2">&quot;avgcount&quot;</span><span class="p">:</span><span class="mi">0</span><span class="p">,</span><span class="s2">&quot;sum&quot;</span><span class="p">:</span><span class="mf">0.000000000</span><span class="p">,</span><span class="s2">&quot;avgtime&quot;</span><span class="p">:</span><span class="mf">0.000000000</span><span class="p">},</span><span class="s2">&quot;opf_sync&quot;</span><span class="p">:{</span><span class="s2">&quot;avgcount&quot;</span><span class="p">:</span><span class="mi">0</span><span class="p">,</span><span class="s2">&quot;sum&quot;</span><span class="p">:</span><span class="mf">0.000000000</span><span class="p">,</span><span class="s2">&quot;avgtime&quot;</span><span class="p">:</span><span class="mf">0.000000000</span><span class="p">},</span><span class="s2">&quot;opf_filesize&quot;</span><span class="p">:{</span><span class="s2">&quot;avgcount&quot;</span><span class="p">:</span><span class="mi">2</span><span class="p">,</span><span class="s2">&quot;sum&quot;</span><span class="p">:</span><span class="mf">0.000000000</span><span class="p">,</span><span class="s2">&quot;avgtime&quot;</span><span class="p">:</span><span class="mf">0.000000000</span><span class="p">},</span><span class="s2">&quot;opf_lock&quot;</span><span class="p">:{</span><span class="s2">&quot;avgcount&quot;</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span><span class="s2">&quot;sum&quot;</span><span class="p">:</span><span class="mf">0.158001360</span><span class="p">,</span><span class="s2">&quot;avgtime&quot;</span><span class="p">:</span><span class="mf">0.158001360</span><span class="p">},</span><span class="s2">&quot;opf_unlock&quot;</span><span class="p">:{</span><span class="s2">&quot;avgcount&quot;</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span><span class="s2">&quot;sum&quot;</span><span class="p">:</span><span class="mf">0.101000871</span><span class="p">,</span><span class="s2">&quot;avgtime&quot;</span><span class="p">:</span><span class="mf">0.101000871</span><span class="p">},</span><span class="s2">&quot;opf_checkreservedlock&quot;</span><span class="p">:{</span><span class="s2">&quot;avgcount&quot;</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span><span class="s2">&quot;sum&quot;</span><span class="p">:</span><span class="mf">0.002000017</span><span class="p">,</span><span class="s2">&quot;avgtime&quot;</span><span class="p">:</span><span class="mf">0.002000017</span><span class="p">},</span><span class="s2">&quot;opf_filecontrol&quot;</span><span class="p">:{</span><span class="s2">&quot;avgcount&quot;</span><span class="p">:</span><span class="mi">4</span><span class="p">,</span><span class="s2">&quot;sum&quot;</span><span class="p">:</span><span class="mf">0.000000000</span><span class="p">,</span><span class="s2">&quot;avgtime&quot;</span><span class="p">:</span><span class="mf">0.000000000</span><span class="p">},</span><span class="s2">&quot;opf_sectorsize&quot;</span><span class="p">:{</span><span class="s2">&quot;avgcount&quot;</span><span class="p">:</span><span class="mi">0</span><span class="p">,</span><span class="s2">&quot;sum&quot;</span><span class="p">:</span><span class="mf">0.000000000</span><span class="p">,</span><span class="s2">&quot;avgtime&quot;</span><span class="p">:</span><span class="mf">0.000000000</span><span class="p">},</span><span class="s2">&quot;opf_devicecharacteristics&quot;</span><span class="p">:{</span><span class="s2">&quot;avgcount&quot;</span><span class="p">:</span><span class="mi">4</span><span class="p">,</span><span class="s2">&quot;sum&quot;</span><span class="p">:</span><span class="mf">0.000000000</span><span class="p">,</span><span class="s2">&quot;avgtime&quot;</span><span class="p">:</span><span class="mf">0.000000000</span><span class="p">}},</span><span class="s2">&quot;libcephsqlite_striper&quot;</span><span class="p">:{</span><span class="s2">&quot;update_metadata&quot;</span><span class="p">:</span><span class="mi">0</span><span class="p">,</span><span class="s2">&quot;update_allocated&quot;</span><span class="p">:</span><span class="mi">0</span><span class="p">,</span><span class="s2">&quot;update_size&quot;</span><span class="p">:</span><span class="mi">0</span><span class="p">,</span><span class="s2">&quot;update_version&quot;</span><span class="p">:</span><span class="mi">0</span><span class="p">,</span><span class="s2">&quot;shrink&quot;</span><span class="p">:</span><span class="mi">0</span><span class="p">,</span><span class="s2">&quot;shrink_bytes&quot;</span><span class="p">:</span><span class="mi">0</span><span class="p">,</span><span class="s2">&quot;lock&quot;</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span><span class="s2">&quot;unlock&quot;</span><span class="p">:</span><span class="mi">1</span><span class="p">}}</span>
</pre></div>
</div>
<p>You may easily manipulate that information using the <a class="reference external" href="https://www.sqlite.org/json1.html">JSON1 extension</a>:</p>
<div class="highlight-sql notranslate"><div class="highlight"><pre><span></span><span class="k">SELECT</span> <span class="n">json_extract</span><span class="p">(</span><span class="n">ceph_perf</span><span class="p">(),</span> <span class="s1">&#39;$.libcephsqlite_vfs.opf_sync.avgcount&#39;</span><span class="p">);</span>
</pre></div>
</div>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="mi">776</span>
</pre></div>
</div>
<p>That tells you the number of times SQLite has called the xSync method of the
<a class="reference external" href="https://www.sqlite.org/c3ref/io_methods.html">SQLite IO Methods</a> of the VFS (for <strong>all</strong> open database connections in the
process). You could analyze the performance stats before and after a number of
queries to see the number of file system syncs required (this would just be
proportional to the number of transactions). Alternatively, you may be more
interested in the average latency to complete a write:</p>
<div class="highlight-sql notranslate"><div class="highlight"><pre><span></span><span class="k">SELECT</span> <span class="n">json_extract</span><span class="p">(</span><span class="n">ceph_perf</span><span class="p">(),</span> <span class="s1">&#39;$.libcephsqlite_vfs.opf_write&#39;</span><span class="p">);</span>
</pre></div>
</div>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">{</span><span class="s2">&quot;avgcount&quot;</span><span class="p">:</span><span class="mi">7873</span><span class="p">,</span><span class="s2">&quot;sum&quot;</span><span class="p">:</span><span class="mf">0.675005797</span><span class="p">,</span><span class="s2">&quot;avgtime&quot;</span><span class="p">:</span><span class="mf">0.000085736</span><span class="p">}</span>
</pre></div>
</div>
<p>Which would tell you there have been 7873 writes with an average
time-to-complete of 85 microseconds. That clearly shows the calls are executed
asynchronously. Returning to sync:</p>
<div class="highlight-sql notranslate"><div class="highlight"><pre><span></span><span class="k">SELECT</span> <span class="n">json_extract</span><span class="p">(</span><span class="n">ceph_perf</span><span class="p">(),</span> <span class="s1">&#39;$.libcephsqlite_vfs.opf_sync&#39;</span><span class="p">);</span>
</pre></div>
</div>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">{</span><span class="s2">&quot;avgcount&quot;</span><span class="p">:</span><span class="mi">776</span><span class="p">,</span><span class="s2">&quot;sum&quot;</span><span class="p">:</span><span class="mf">4.802041199</span><span class="p">,</span><span class="s2">&quot;avgtime&quot;</span><span class="p">:</span><span class="mf">0.006188197</span><span class="p">}</span>
</pre></div>
</div>
<p>6 milliseconds were spent on average executing a sync call. This gathers all of
the asynchronous writes as well as an asynchronous update to the size of the
striped file.</p>
</div>
</div>



           </div>
           
          </div>
          <footer>
    <div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
        <a href="../objclass-sdk/" class="btn btn-neutral float-right" title="SDK for Ceph Object Classes" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right" aria-hidden="true"></span></a>
        <a href="../python/" class="btn btn-neutral float-left" title="Librados (Python)" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left" aria-hidden="true"></span> Previous</a>
    </div>

  <hr/>

  <div role="contentinfo">
    <p>
        &#169; Copyright 2016, Ceph authors and contributors. Licensed under Creative Commons Attribution Share Alike 3.0 (CC-BY-SA-3.0).

    </p>
  </div> 

</footer>
        </div>
      </div>

    </section>

  </div>
  

  <script type="text/javascript">
      jQuery(function () {
          SphinxRtdTheme.Navigation.enable(true);
      });
  </script>

  
  
    
   

</body>
</html>